package com.conversationboard.site;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import com.conversationboard.cache.newanswers.INewAnswersCache;
import com.conversationboard.cache.newanswers.NewAnswersCache;
import com.conversationboard.cache.newanswers.NewAnswersThread;
import com.conversationboard.cache.readcounter.ThreadCounter;
import com.conversationboard.database.Database;
import com.conversationboard.database.PerformanceRecord;
import com.conversationboard.database.Queries;
import com.conversationboard.model.PendingRegistrations;
import com.conversationboard.model.Substitutions;
import com.conversationboard.model.databaseinfo.DatabaseThread;
import com.conversationboard.model.propertybag.Property;
import com.conversationboard.model.propertybag.PropertyBag;

public class Site {

	private static boolean allowRegistrations = true;
	private static Substitutions substitutions = new Substitutions();
	private static INewAnswersCache siteNewAnswers;
	private static Map<String, PerformanceRecord> databaseConnectionInfo = Collections.synchronizedMap(new HashMap<String, PerformanceRecord>());
	private static long pageHitCount = 0;
	private static long showAllMessagesHitCount = 0;
	private static long cacheHitCount = 0;
	private static long cacheMissCount = 0;
	private static Date startupTime = new Date();
	private static long messageCount = 0;
	private static long threadCount = 0;
	private static long userCount = 0;
	private static List<ThreadCounter> mostPopularThreads = new CopyOnWriteArrayList<ThreadCounter>();
	private static String thread = "Question";
	private static String post = "Answer";
	private static int currentlyActiveUsers;

	static {

		try {

			/* Initialise Site New Answers Cache */

			siteNewAnswers = new NewAnswersCache(0, false);

			Property property = PropertyBag.get("allowregistrations");

			if (property != null) {
				String allow = property.getValue();

				if (allow.equalsIgnoreCase("true")) {
					allowRegistrations = true;
				} else {
					allowRegistrations = false;
				}
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}
	}


	public static List<ThreadCounter> getMostPopularThreads() {
		return mostPopularThreads;
	}


	public static void setMostPopularThreads(List<ThreadCounter> mostPopularThreads) {
		Site.mostPopularThreads = mostPopularThreads;
	}


	public static long getShowAllMessagesHitCount() {
		return showAllMessagesHitCount;
	}


	public static void incrementShowAllMessagesHitCount() {
		showAllMessagesHitCount++;
	}


	public static Date getStartupTime() {
		return startupTime;
	}


	public static void recordDatabaseConnectionInfo(String callerName, long time) {

		/* This next line should not be needed, but it seems to be... At least, on OS X */

		if (databaseConnectionInfo == null) {
			databaseConnectionInfo = Collections.synchronizedMap(new HashMap<String, PerformanceRecord>());
		}

		if (databaseConnectionInfo.containsKey(callerName)) {

			PerformanceRecord record = databaseConnectionInfo.get(callerName);
			record.incrementTime(time);
			record.incrementCount();

			databaseConnectionInfo.put(callerName, record);

		} else {

			PerformanceRecord record = new PerformanceRecord();
			record.setCallerName(callerName);
			record.incrementCount();
			record.incrementTime(time);

			databaseConnectionInfo.put(callerName, record);

		}

	}


	public static Map<String, PerformanceRecord> getDatabaseConnectionInfo() {
		return databaseConnectionInfo;
	}


	public static void incrementCacheHitCount() {

		if (cacheHitCount > 1000000000000000000l) {
			cacheHitCount = 0;
		}

		cacheHitCount++;

	}


	public static long getCacheHitCount() {
		return cacheHitCount;
	}


	public static void incrementCacheMissCount() {

		if (cacheMissCount > 1000000000000000000l) {
			cacheMissCount = 0;
		}

		cacheMissCount++;

	}


	public static long getCacheMissCount() {
		return cacheMissCount;
	}


	public static void incrementPageHitCount() {

		if (pageHitCount > 1000000000000000000l) {
			pageHitCount = 0;
		}

		pageHitCount++;
	}


	public static long getPageHitCount() {
		return pageHitCount;
	}


	public synchronized static void allowRegistrations(boolean allow) throws SQLException {
		allowRegistrations = allow;

		if (allow) {
			PropertyBag.put("allowregistrations", "true");
		} else {
			PropertyBag.put("allowregistrations", "false");
		}

	}


	public static boolean isRegistrationAllowed() {
		return allowRegistrations;
	}


	public synchronized static int getPendingRegistrations() throws SQLException {

		PendingRegistrations pendingRegistrations = PendingRegistrations.getInstance();

		return pendingRegistrations.size();

	}


	public static Substitutions getSubstitutions() {
		return substitutions;
	}


	public static INewAnswersCache getSiteNewAnswers() throws SQLException {

		if (siteNewAnswers.size() == 0) {
			Site.initialiseSiteNewAnswersCache();
		}

		return siteNewAnswers;
	}


	public static void initialiseSiteNewAnswersCache() throws SQLException {
		siteNewAnswers.populate();
	}


	public static List<NewAnswersThread> getSiteNewQuestions(int start, int pageSize) throws SQLException {

		Connection connection = Database.getConnection();

		List<NewAnswersThread> threads = new LinkedList<NewAnswersThread>();

		try {

			PreparedStatement statement = connection.prepareStatement(Queries.GET_ALL_RECENT_QUESTIONS_QUERY);

			statement.setInt(1, start);
			statement.setInt(2, pageSize);

			statement.execute();
			ResultSet resultSet = statement.getResultSet();

			while (resultSet.next()) {

				int boardId = resultSet.getInt("boardid");
				int threadId = resultSet.getInt("threadid");

				NewAnswersThread thread = new NewAnswersThread(boardId, threadId);

				thread.setTimestamp(resultSet.getTimestamp("timestamp"));
				thread.setLastUpdated(resultSet.getTimestamp("lastupdated"));
				thread.setCreatorId(resultSet.getString("displayname"));
				thread.setTitle(resultSet.getString("title"));
				thread.setDeleted(resultSet.getBoolean("deleted"));
				thread.setLocked(resultSet.getBoolean("locked"));
				thread.setWorksafe(resultSet.getBoolean("worksafe"));
				thread.setUnanswered(resultSet.getBoolean("unanswered"));
				thread.setPoll(resultSet.getBoolean("poll"));
				thread.setPollClosingDate(resultSet.getDate("pollclosingdate"));
				threads.add(thread);
			}

			resultSet.close();

		} finally {
			Database.close(connection);
		}

		return threads;

	}


	public static boolean isShut() throws SQLException {

		Property property = PropertyBag.get("siteshut");

		if ((property == null) || (property.getValue().equalsIgnoreCase("true"))) {
			return true;
		} else {
			return false;
		}
	}


	public static void setShut(boolean shut) throws SQLException {

		if (shut) {
			PropertyBag.put("siteshut", "true");
		} else {
			PropertyBag.put("siteshut", "false");
		}

	}


	public static Map<String, String> getDatabaseStatus() throws SQLException {

		Connection connection = null;

		Map<String, String> status = new HashMap<String, String>();

		try {

			connection = Database.getConnection();

			PreparedStatement statement = connection.prepareStatement(Queries.SHOW_STATUS);
			statement.execute();

			ResultSet resultSet = statement.getResultSet();

			while (resultSet.next()) {
				status.put(resultSet.getString(1), resultSet.getString(2));
			}

			return status;

		} finally {
			Database.close(connection);
		}
	}


	public static List<DatabaseThread> getDatabaseThreadStatus() throws SQLException {

		Connection connection = null;

		List<DatabaseThread> threads = new LinkedList<DatabaseThread>();

		try {

			connection = Database.getConnection();

			PreparedStatement statement = connection.prepareStatement(Queries.SHOW_STATUS);
			statement.execute();

			ResultSet resultSet = statement.getResultSet();

			while (resultSet.next()) {

			}

			return threads;

		} finally {
			Database.close(connection);
		}
	}


	public static List<DatabaseThread> getDatabaseThreadList() throws SQLException {

		Connection connection = null;
		List<DatabaseThread> threads = new LinkedList<DatabaseThread>();

		try {

			connection = Database.getConnection();

			PreparedStatement statement = connection.prepareStatement(Queries.SHOW_PROCESS_LIST);
			statement.execute();

			ResultSet resultSet = statement.getResultSet();

			while (resultSet.next()) {

				DatabaseThread databaseThread = new DatabaseThread();
				databaseThread.setId(resultSet.getInt("id"));
				databaseThread.setUser(resultSet.getString("user"));
				databaseThread.setHost(resultSet.getString("host"));
				databaseThread.setDatabase(resultSet.getString("db"));
				databaseThread.setCommand(resultSet.getString("command"));
				databaseThread.setTime(resultSet.getInt("time"));
				databaseThread.setState(resultSet.getString("state"));

				threads.add(databaseThread);
			}

			return threads;

		} finally {
			Database.close(connection);
		}

	}


	public static long getMessageCount() {
		return messageCount;
	}


	public static void setMessageCount(long messageCount) {
		Site.messageCount = messageCount;
	}


	public static long getThreadCount() {
		return threadCount;
	}


	public static void setThreadCount(long threadCount) {
		Site.threadCount = threadCount;
	}


	public static long getUserCount() {
		return userCount;
	}


	public static void setUserCount(long userCount) {
		Site.userCount = userCount;
	}


	public static String getThread() {
		return thread;
	}


	public static void setThread(String thread) {
		Site.thread = thread;
	}


	public static String getPost() {
		return post;
	}


	public static void setPost(String post) {
		Site.post = post;
	}


	public static int getCurrentlyActiveUsers() {
		return currentlyActiveUsers;
	}


	public static void setCurrentlyActiveUsers(int currentlyLoggedInUsers) {
		Site.currentlyActiveUsers = currentlyLoggedInUsers;
	}


	public static void incrementMessageCount() {
		messageCount++;
	}


	public static void incrementThreadCount() {
		threadCount++;
	}


	public static void incrementUserCount() {
		userCount++;
	}

}
